package com.yuandian.bpm.bi.modules.quartz.util;

import com.sun.management.OperatingSystemMXBean;
import com.yuandian.bpm.bi.modules.quartz.vo.SystemPerformanceInfo;
import lombok.extern.slf4j.Slf4j;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * 项目名称：拨测系统
 * 模块名称：收集硬件信息（cpu、内存、硬盘）
 * 开发人员：tianlr
 * 日    期：2022/09/16
 */
@Slf4j
public class CollectHardwareUtils {

	private static final long B = 1024;

	public static final String FILES_SHELL = "df -k";


	public static void main(String[] args) throws InterruptedException {
		System.out.println("IP: " + getInet4Address());
		System.out.println("进程ID: " + getProcessID());
		System.out.println("磁盘大小: " + getDiskInfo().totalSize + "KB, 磁盘已使用大小: " + getDiskInfo().usedSize+ "KB, 占比: " + getDiskInfo().proportion);
		System.out.println("总物理内存大小: " + getMemoryInfo().totalSize + "KB, 已使用的物理内存: " + getMemoryInfo().usedSize + "KB, 占比: " + getMemoryInfo().proportion);
		System.out.println("线程总数: " + getTotalThread() + " , CPU使用率: " + getCpuRatio());

		System.out.println("磁盘大小: " + getAllDiskInfo().totalSize + "B, 磁盘已使用大小: " +
				getAllDiskInfo().usedSize+ "B, 占比: " + getAllDiskInfo().proportion);


	}

	public static String getInet4Address() {
		Enumeration<NetworkInterface> nis;
		String ip = null;
		try {
			nis = NetworkInterface.getNetworkInterfaces();
			for (; nis.hasMoreElements();) {
				NetworkInterface ni = nis.nextElement();
				Enumeration<InetAddress> ias = ni.getInetAddresses();
				for (; ias.hasMoreElements();) {
					InetAddress ia = ias.nextElement();
					//ia instanceof Inet6Address && !ia.equals("")
					if (ia instanceof Inet4Address && !ia.getHostAddress().equals("127.0.0.1")) {
						ip = ia.getHostAddress();
					}
				}
			}
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ip;
	}


	/**
	 * 获取进程ID
	 *
	 * @return
	 */
	public static int getProcessID() {
		RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
		return Integer.valueOf(runtimeMXBean.getName().split("@")[0]);
	}

	/**
	 * 获取总物理内存大小
	 *
	 * @return
	 */
	public static ObjectData<Long, Long, Double> getMemoryInfo() {
		OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
		// 总内存大小
		long totalMemorySize = osmxb.getTotalPhysicalMemorySize();
		// 剩余内存大小
		long freePhysicalMemorySize = osmxb.getFreePhysicalMemorySize();
		// 已使用内存大小
		long usedMemorySize = totalMemorySize - freePhysicalMemorySize;
		// 计算占用
		Double proportion = calculation(usedMemorySize, totalMemorySize);

		return new ObjectData<>(totalMemorySize, usedMemorySize, proportion);
	}

	/**
	 * 磁盘信息
	 * @return
	 */
	public static ObjectData<Long, Long, Double> getDiskInfo() {

		File[] files = File.listRoots();
		// 总大小 			已使用大小	   可用大小
		long diskTotal = 0, diskUsed = 0, UsableSpace = 0;
		for (File file : files) {
			diskTotal += file.getTotalSpace();
			UsableSpace += file.getUsableSpace();
		}
		diskUsed = diskTotal - UsableSpace;
		// 计算占用
		Double proportion = calculation(diskUsed, diskTotal);
		return new ObjectData<>(diskTotal, diskUsed, proportion);
	}

	/**
	 * 获取所有磁盘大小
	 *
	 * Filesystem              1M-blocks  Used Available Use% Mounted on
	 * devtmpfs                     3900     0      3900   0% /dev
	 * tmpfs                        3911     0      3911   0% /dev/shm
	 * tmpfs                        3911    10      3902   1% /run
	 * tmpfs                        3911     0      3911   0% /sys/fs/cgroup
	 * /dev/mapper/centos-root      9952  2203      7222  24% /
	 * /dev/sda1                    1952   124      1711   7% /boot
	 * /dev/mapper/centos-data    490734   973    464811   1% /data
	 * tmpfs                         783     0       783   0% /run/user/0
	 *
	 * @return
	 */
	public static ObjectData<Long, Long, Double> getAllDiskInfo() {
		// 总大小 			已使用大小
		long diskTotal = 0, diskUsed = 0;
		Double proportion = 0d;
		try {
			String line = "", osName = "";
			Process pro = Runtime.getRuntime().exec(FILES_SHELL);
			int code = pro.waitFor();
			if (code == 0) {
				List<String> lineString = new ArrayList<>();

//				String filePath = "D:/Test03.txt";
//				FileInputStream file = new FileInputStream(filePath);
//				BufferedReader br = new BufferedReader(new InputStreamReader(file, "GBK"));
				BufferedReader br = new BufferedReader(new InputStreamReader(pro.getInputStream()));
				while ((line = br.readLine()) != null) {
					if (!line.equals("")) {
						lineString.add(line);
					}
				}

				// Filesystem              1M-blocks  Used Available Use% Mounted on
				// devtmpfs                     3900     0      3900   0% /dev
				int index = 0;
				for (String lineStr : lineString) {
					// 第一行不读取
					if (index == 0) {
						index++;
						continue;
					}
					String[] s = lineStr.split(" ");
					int flag = 0;
					for (String value : s) {
						// 只需要获取 总量 和 已使用量 即可
						if (flag >= 2) { break; }

						if (flag == 0 && isNumeric(value)) {
							diskTotal += Long.parseLong(value);
							flag++;
						} else if (flag == 1 && isNumeric(value)) {
							diskUsed += Long.parseLong(value);
							flag++;
						}
					}
				}

			} else {
				log.warn("[设备状态上报任务][获取磁盘大小]失败,返回码pro.waitFor(): {}", 1);
			}

			// 计算占用
			proportion = calculation(diskUsed, diskTotal);

		} catch (Exception e) {
			e.printStackTrace();
			log.error("[设备状态上报任务][获取磁盘大小]报错", e);
		}
		return new ObjectData<>((diskTotal * B), (diskUsed * B), proportion);
	}

	/**
	 * 判断字符串是否为数字
	 * @param str
	 * @return
	 */
	public static boolean isNumeric(String str) {
		try {
			new BigDecimal(str).toString();
		} catch (Exception e) {
			//异常 说明包含非数字
			return false;
		}
		return true;
	}

	/**
	 * 获得整个服务器所有虚拟线程总数
	 * @return
	 */
	public static long getTotalThread() {

		SystemInfo si = new SystemInfo();
		OperatingSystem os = si.getOperatingSystem();

		return os.getThreadCount();
	}

	public static SystemPerformanceInfo getSystemInfo() {
		SystemPerformanceInfo systemInfo = new SystemPerformanceInfo();
		SystemInfo si = new SystemInfo();
		OperatingSystem os = si.getOperatingSystem();
		HardwareAbstractionLayer hal = si.getHardware();
		CentralProcessor processor = hal.getProcessor();

		// 系统总线程数
		systemInfo.setSysThreadTotal(os.getThreadCount());
		// 物理核数
		systemInfo.setPhysicalCoreTotal(processor.getPhysicalProcessorCount());
		// 逻辑核数
		systemInfo.setLogicCoreTotal(processor.getLogicalProcessorCount());

		long[] prevTicks = processor.getSystemCpuLoadTicks();
		// Wait a second...
		Util.sleep(3000);
		long[] ticks = processor.getSystemCpuLoadTicks();
		long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
		long sys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
		long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
		long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
		long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
		long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
		long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
		long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
		long totalCpu = user + nice + sys + idle + iowait + irq + softirq + steal;

		systemInfo.setUserCpuLoadRate(100d * user / totalCpu);
		systemInfo.setSystemCpuLoadRate(100d * sys / totalCpu);

		return systemInfo;
	}



	/**
	 * 获取CPU使用率
	 * @return
	 */
	public static double getCpuRatio() {
		double cpuRatio = 0;
		// 第一种方法
		OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
		NumberFormat percentIns = NumberFormat.getPercentInstance();
		percentIns.setMinimumFractionDigits(2);
		percentIns.setRoundingMode(RoundingMode.FLOOR);
		cpuRatio = twoDecimal(osmxb.getSystemCpuLoad() * 100);

		return cpuRatio;
	}

	public static double twoDecimal(double doubleValue) {
		BigDecimal bigDecimal = new BigDecimal(doubleValue).setScale(2, RoundingMode.HALF_UP);
		return bigDecimal.doubleValue();
	}

	/**
	 * 相除保留两位小数
	 * @param dividend 被除数
	 * @param divisor 除数
	 * @return
	 */
	public static Double calculation(Long dividend, Long divisor) {
		if (divisor == 0) { return 0d; }
		return new BigDecimal((double) dividend * 100 / divisor).setScale(2, RoundingMode.HALF_UP).doubleValue();
	}

}

/**
 * 元组数据，用于一个函数返回不同值
 */
class ObjectData<A, B, C> {

	/** 总大小 */
	public final A totalSize;

	/** 已使用大小 */
	public final B usedSize;

	/** 占比 */
	public final C proportion;

	ObjectData(A a, B b, C c) {
		this.totalSize = a;
		this.usedSize = b;
		this.proportion = c;
	}

	@Override
	public String toString() {
		return "ObjectData{" +
				"totalSize=" + totalSize +
				", usedSize=" + usedSize +
				", proportion=" + proportion +
				'}';
	}
}
